import json
import logging
import os
import stat
import time
import uuid
import paramiko
import queue
import threading
from service import mysql_service


class SFTPClient():
    def __init__(self, task_id, ip_id):
        self.db = mysql_service()
        self.task_id = task_id
        self.ip_id = ip_id
        self.service = 'sftp'
        sql = "SELECT * FROM `ip_list_%s` WHERE `id` = %s"
        info:dict = self.db.get_one_data(sql=sql, params=(self.task_id, self.ip_id))
        if info:
            config = info.get('sftp_config')
            config = json.loads(config)
            self.hostname = config.get('hostname')
            self.port = config.get('port')
            self.username = config.get('username')
            self.password = config.get('password')
            self.client = paramiko.SSHClient()
            self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self.client.connect(hostname=self.hostname, username=self.username, password=self.password, port=self.port, timeout=10)
            self.sftp = self.client.open_sftp()
        self.dir_queue = queue.Queue()
        self.download_file_queue = queue.Queue()
        self.task_uuid = str(uuid.uuid4())
        self.task_folder_path = './save_files/' + self.task_uuid
        if not os.path.exists(self.task_folder_path):
            os.makedirs(self.task_folder_path)
        self.save_file_type = ['pdf', 'doc', 'docx', 'txt']
        self.scaned_path = set()
        self.downloaded_file = set()
        self.scaned_path_nums = 0
        self.thread_nums = 10
        self.thread_running_flag = {} # 用于标记线程是否在运行
        self.skip_path = ['/proc', '/sys', '公司/artical_scan', '.local/share/Trash', '.venv']
    
    def save_file_info(self, file_name, file_uuidname, file_save_path, file_path, file_format, file_size):
        sql = """
            INSERT INTO `file_info_%s` 
            (`file_name`, `file_uuidname`, `file_save_path`, `file_path`, `file_format`, `file_size`, `service`, `ip_id`, `port`)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
        self.db.insert_or_update(sql=sql, params=(self.task_id, file_name, 
                                                  file_uuidname, file_save_path, 
                                                  file_path, file_format, file_size,
                                                    self.service, self.ip_id, self.port))

    def download_file(self, remote_path, file_size):
        # logging.info(f'下载文件：【{remote_path}】中...')
        file_name = remote_path.split('/')[-1]
        file_format = file_name.split('.')[-1]
        file_uuidname = str(uuid.uuid4()) + '.' + file_format
        local_path = os.path.join(self.task_folder_path,file_uuidname)
        if self.client:
            try:
                self.sftp.get(remote_path, local_path)
                self.save_file_info(file_name, file_uuidname, local_path, remote_path, file_format, file_size)
                self.downloaded_file.add(remote_path)
                return True
            except Exception as e:
                logging.error(f'{remote_path}：' + str(e))
        return False
    
    def is_dir(self, remote_path):
        file_size = 0
        try:
            file_size = self.sftp.lstat(remote_path).st_size
            st_mode = self.sftp.lstat(remote_path).st_mode
            if stat.S_ISDIR(st_mode):
                return 'dir', file_size
            elif stat.S_ISLNK(st_mode):
                return 'link', file_size
            return 'file', file_size
        except IOError:
            return False, file_size
    
    def get_files(self, remote_path):
        logging.error(f'扫描文件夹：【{remote_path}】中...')
        for path_ in self.skip_path:
            if path_ in remote_path:
                return
        self.scaned_path.add(remote_path)
        for file in self.sftp.listdir(remote_path):
            file_path = os.path.join(remote_path, file)
            flag, file_size = self.is_dir(file_path)
            if flag == 'dir' and file_path not in self.scaned_path:
                self.dir_queue.put(file_path)
            elif flag == 'file':
                file_format = file.split('.')[-1]
                if file_format in self.save_file_type and file_path not in self.downloaded_file:
                    # self.download_file_queue.put((file_path, file_size))
                    self.download_file(file_path, file_size)
        self.scaned_path_nums += 1
    
    def is_any_thread_running(self):
        # logging.error(self.thread_running_flag.values())
        for status in self.thread_running_flag.values():
            if status:
                return True
        return False

    def scan_files(self):
        thread_name = str(uuid.uuid4())
        self.thread_running_flag[thread_name] = False
        while self.is_any_thread_running() or not self.dir_queue.empty():
            try:
                try:
                    dir_path = self.dir_queue.get(timeout=0.1)
                except:
                    continue
                self.thread_running_flag[thread_name] = True
                self.get_files(dir_path)
            except Exception as e:
                logging.error(f'{dir_path}：' + str(e))
            self.thread_running_flag[thread_name] = False
            if not self.scaned_path_nums % 500:
                logging.error('-'*80)
                logging.error(f'待扫描文件夹数量：{self.dir_queue.qsize()}')
                logging.error(f'已扫描文件夹数量：{self.scaned_path_nums}')
                logging.error(f'已下载文件数量：{len(self.downloaded_file)}')
                logging.error(f'进程运行情况：{self.thread_running_flag.values()}')
                logging.error('-'*80)
    
    def download_file_thread(self):
        thread_name = str(uuid.uuid4())
        self.thread_running_flag[thread_name] = False
        while self.is_any_thread_running() or not self.dir_queue.empty() or not self.download_file_queue.empty():
            try:
                try:
                    data = self.download_file_queue.get(timeout=0.1)
                except:
                    continue
                self.thread_running_flag[thread_name] = True
                self.download_file(data[0], data[1])
            except Exception as e:
                logging.error(f'Download【{data[0]}】Error, Filesize【{data[1]}】：' + str(e))
            self.thread_running_flag[thread_name] = False
            
        
            
def main():
    css = SFTPClient(task_id=1, ip_id=1)
    css.dir_queue.put('/mnt/rkcolm/marked_files')
    start_time = time.time()
    css.scan_files()
    end_time = time.time()
    css.client.close()
    logging.error(f'耗时：{end_time - start_time}秒')


### 只能说很遗憾，对于同一个ip，sftp创建的同一个客户端无法实现多线程，所以只能去想其他办法了= = 
if __name__ == '__main__':
    main()
    # css = SFTPClient(task_id=1, ip_id=1)
    # print(css.is_dir('/opt/marked_files'))
    # print(css.is_dir('/mnt/rkcolm/marked_files'))